using Oracle.ManagedDataAccess.Client;
using Polly;
using Polly.Retry;
using System.Data;

namespace Lzfy_Refund_Service.Services
{
    /// <summary>
    /// 具有弹性的Oracle连接工厂
    /// </summary>
    public class ResilientOracleConnectionFactory : IOracleConnectionFactory, IResilientDbConnectionFactory
    {
        private readonly string _connectionString;
        private readonly ILogger<ResilientOracleConnectionFactory> _logger;
        private readonly AsyncRetryPolicy _retryPolicy;

        public ResilientOracleConnectionFactory(
            string connectionString,
            ILogger<ResilientOracleConnectionFactory> logger)
        {
            _connectionString = connectionString;
            _logger = logger;
            
            // 创建重试策略
            _retryPolicy = Policy
                .Handle<OracleException>(ex => IsTransientError(ex))
                .Or<TimeoutException>()
                .WaitAndRetryAsync(
                    retryCount: 3, // 重试3次
                    sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), // 指数退避策略
                    onRetry: (exception, timeSpan, retryCount, context) =>
                    {
                        _logger.LogWarning(exception, 
                            "Oracle连接失败，将在 {RetryTimeSpan} 后进行第 {RetryCount} 次重试。错误: {ErrorMessage}",
                            timeSpan, retryCount, exception.Message);
                    });
        }

        /// <summary>
        /// 创建数据库连接
        /// </summary>
        public IDbConnection CreateConnection()
        {
            var connection = new OracleConnection(_connectionString);
            connection.InfoMessage += (sender, args) =>
            {
                _logger.LogInformation("Oracle信息消息: {Message}", args.Message);
            };
            return connection;
        }

        /// <summary>
        /// 创建并打开数据库连接（带重试机制）
        /// </summary>
        public async Task<IDbConnection> CreateAndOpenConnectionAsync()
        {
            return await _retryPolicy.ExecuteAsync(async () =>
            {
                var connection = CreateConnection() as OracleConnection;
                if (connection == null)
                {
                    throw new InvalidOperationException("无法创建 Oracle 连接");
                }
                await connection.OpenAsync();
                return connection;
            });
        }

        /// <summary>
        /// 判断是否为瞬态错误
        /// </summary>
        private bool IsTransientError(OracleException ex)
        {
            // Oracle瞬态错误代码列表
            int[] transientErrorNumbers =
            {
                // 连接错误
                12154, // TNS无法解析服务名
                12170, // 连接超时
                12500, // TNS监听程序无法启动专用服务器进程
                12505, // TNS监听程序不识别连接描述符中请求的SID
                12519, // TNS:没有可用的适当服务处理程序
                12520, // TNS:监听程序无法找到请求的SID中可用的处理程序
                12521, // TNS:监听程序不知道实例请求的服务处理程序
                12525, // TNS:监听程序无法获取实例的状态
                12535, // TNS:操作超时
                12547, // TNS:丢失连接
                12560, // TNS:协议适配器错误
                12571, // TNS:包写入器故障
                12606, // TNS:应用程序超时发生
                12637, // 包读取器故障
                12641, // 认证服务失败
                
                // 网络错误
                3113,  // 通信通道的文件结尾
                3114,  // 与Oracle通信时没有收到
                3135,  // 连接丢失
                
                // 资源限制
                1033,  // Oracle正在启动或关闭
                1034,  // Oracle未启动
                1089,  // 系统关闭立即生效
                1090,  // 系统正在关闭
                
                // 死锁和超时
                60,    // 死锁检测到
                4020,  // 超时发生
                4021,  // 超时发生
                30006, // 资源忙
                
                // 其他可重试错误
                24756, // 事务已回滚
                25408, // 无法生成唯一服务器组名称
                29276, // 转移连接失败
                24347, // 无法连接到目标实例
            };

            return transientErrorNumbers.Contains(ex.Number);
        }
    }
}